﻿using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using RG.Common.Export;
using RG.Common.WPF.BaseSelector;
using Microsoft.Windows.Controls;

namespace RG.Common.WPF.Controls.ColumnInfoDataGrid
{
    public class ColumnInfoDataGrid : DataGrid
    {
        
        private readonly List<ColumnInfoSortDescription> sortDescriptions;

        public ColumnInfoDataGrid()
        {
            sortDescriptions = new List<ColumnInfoSortDescription>();
            AutoGenerateColumns = false;
            IsReadOnly = true;
            AlternatingRowBackground = Brushes.AliceBlue;
            AlternationCount = 2;
            Sorting += ColumnInfoDataGrid_Sorting;
            MouseUp += ColumnInfoDataGrid_MouseUp;
            MouseLeftButtonDown += CIDG_MouseLeftButtonDown;
            ColumnReordered += ColumnInfoDataGrid_Reordered;
        }

        private void ColumnInfoDataGrid_Reordered(object sender, DataGridColumnEventArgs e)
        {
            DoSaveColumnsPreset();
        }

        void DoSaveColumnsPreset()
        {
            var grid = this;
            var columnPreset = new List<ColumnPreset>();
            if (grid.Columns == null || grid.Columns.Count == 0 || grid.Columns[0].DisplayIndex < 0) return;
            foreach (var col in grid.Columns)
            {
                columnPreset.Add(new ColumnPreset
                {
                    DisplayPosition = col.DisplayIndex,
                    Header = col.Header.ToString(),
                    Width = (decimal) col.Width.DisplayValue,
                    Visible = col.Visibility
                });
            }
            grid.ColumnPreset = columnPreset;
        }

        void ColumnInfoDataGrid_MouseUp(object sender, MouseEventArgs e)
        {
            DoSaveColumnsPreset();
            var text = GetCellDisplayValue((DependencyObject)e.OriginalSource);
            if (text != null) CurentCellValue = text;
        }

        void CIDG_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            var text = GetCellDisplayValue((DependencyObject)e.OriginalSource);
            if (text != null) CurentCellValue = text;
        }

        string GetCellDisplayValue(DependencyObject dep)
        {
            while (dep != null && !(dep is DataGridCell))
                dep = VisualTreeHelper.GetParent(dep);
            if (dep == null) return null;

            var cell = (DataGridCell) dep;
            var NamedPresenter = cell.DataContext as INamed;
            if (NamedPresenter != null) return NamedPresenter.Name;
            return cell.Content is TextBlock
                ? ((TextBlock) cell.Content).Text
                : string.Empty;
        }

        void ColumnInfoDataGrid_Sorting(object sender, DataGridSortingEventArgs e)
        {
            e.Handled = true; // prevent the built-in sort from sorting
            PerformCustomSort(e.Column);
            DoSaveColumnsPreset();
        }

        private static int FindSortDescription(IEnumerable<ColumnInfoSortDescription> sortDescriptions, IColumnInfo column)
        {
            var i = 0;
            foreach (var sortDesc in sortDescriptions)
            {
                if (sortDesc.Column == column)
                    return i;
                i++;
            }

            return -1;
        }

        private void PerformCustomSort(DataGridColumn column)
        {
            var info = ColumnInfoAttachedProperty.GetColumnInfo(column);

            var idx = FindSortDescription(sortDescriptions, info);

            if (idx < 0)
            {
                if (Keyboard.Modifiers != ModifierKeys.Shift)
                    sortDescriptions.Clear();

                column.SortDirection = ListSortDirection.Ascending;
                sortDescriptions.Add(new ColumnInfoSortDescription(info));
            }
            else
            {
                if (column.SortDirection == ListSortDirection.Descending)
                {
                    column.SortDirection = null;
                    sortDescriptions.RemoveAt(idx);
                }
                else
                {
                    column.SortDirection = (column.SortDirection != ListSortDirection.Ascending)
                                               ? ListSortDirection.Ascending
                                               : ListSortDirection.Descending;

                    sortDescriptions[idx].Direction = column.SortDirection.Value;
                }
            }

            var list = ItemsSource ?? new List<int>();

            var lcv = (ListCollectionView)CollectionViewSource.GetDefaultView(list);

            lcv.CustomSort = new ColumnInfoSort(sortDescriptions);
        }

        public static readonly DependencyProperty ColumnInfoProperty = DependencyProperty.Register(
            "ColumnInfo", typeof(IEnumerable<IColumnInfo>), typeof(ColumnInfoDataGrid), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender, OnColummnsChanged));

        public IEnumerable<IColumnInfo> ColumnInfo
        {
            get { return (IEnumerable<IColumnInfo>)GetValue(ColumnInfoProperty); }
            set { SetValue(ColumnInfoProperty, value); }
        }

        public static readonly DependencyProperty ColumnPresetProperty = DependencyProperty.Register(
            "ColumnPreset", typeof(IEnumerable<ColumnPreset>), typeof(ColumnInfoDataGrid), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.AffectsRender, OnPresetChange));

        public IEnumerable<ColumnPreset> ColumnPreset
        {
            get { return (IEnumerable<ColumnPreset>)GetValue(ColumnPresetProperty); }
            set { SetValue(ColumnPresetProperty, value); }
        }


        public static readonly DependencyProperty CurentCellValueProperty = DependencyProperty.Register(
           "CurentCellValue", typeof(string), typeof(ColumnInfoDataGrid), new FrameworkPropertyMetadata(""));

        public string CurentCellValue
        {
            get { return (string)GetValue(CurentCellValueProperty); }
            set { SetValue(CurentCellValueProperty, value); }
        }

        private static void OnPresetChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var grid = d as ColumnInfoDataGrid;
            var presets = e.NewValue as IEnumerable<ColumnPreset>;
            if (grid == null || presets == null) return;
            // применяем ранее сохранённые настройки
            // ToDo: подумать, как корректно переделать добавление (если больше 1 добавлям - путаются позиции)
            // ToDo: и удаление (сейчас на её место встаёт последняя) колонок
            var newColumns =
                grid.Columns.Where(x => presets.FirstOrDefault(y => y.Header == x.Header.ToString()) == null).Select(
                    x => grid.Columns.IndexOf(x)).ToList();
            foreach (var column in grid.Columns)
            {
                var cp = presets.FirstOrDefault(x => x.Header == column.Header.ToString());
                if (cp != null)
                {
                    var di = cp.DisplayPosition + newColumns.Where(x => cp.DisplayPosition >= x).ToList().Count();
                    if (di < grid.Columns.Count)
                        column.DisplayIndex = di;
                    column.Visibility = cp.Visible;
                    column.Width = (double)cp.Width;
                }
            }
            grid.ColumnPreset = presets;
        }

        private static void OnColummnsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var grid = d as ColumnInfoDataGrid;
            var columns = e.NewValue as IEnumerable<IColumnInfo>;

            if (grid == null || columns == null) return;

            grid.Columns.Clear();
            int i = 0;
            foreach (var info in columns)
            {
                // TODO Не очень красиво
                DataGridColumn newColumn = grid.CreateNewColumn(info);
                newColumn.IsReadOnly = true;
                newColumn.SortMemberPath = "c" + i++;
                newColumn.Header = info.Header;
                switch (info.Width)
                {
                    case ColumnGridWidthType.Star:
                        newColumn.Width = new DataGridLength(70, DataGridLengthUnitType.Star);
                        break;
                    case ColumnGridWidthType.Auto:
                        newColumn.Width = new DataGridLength(70, DataGridLengthUnitType.Auto);
                        break;
                    default:
                        newColumn.Width = info.Width;
                        break;
                }
                ColumnInfoAttachedProperty.SetColumnInfo(newColumn, info);
                grid.Columns.Add(newColumn);
            }
        }

        private DataGridColumn CreateNewColumn(IColumnInfo info)
        {
            bool isReadOnly = ColumnInfoTemplates.IsStandart(info.TemplateName) && info.TemplateName != "bool";
            BindingBase bindingBase;
            if (!isReadOnly)
            {
                bindingBase = new Binding(info.PropertyPath);
            }
            else
            {
                var binding = new MultiBinding {Converter = new LambdaMultiConverter(info)};
                binding.Bindings.Add(new Binding());
                binding.Bindings.Add(new Binding(info.PropertyPath));
                bindingBase = binding;
            }
            //PresentationTraceSources.SetTraceLevel(binding, PresentationTraceLevel.High);

            //TODO Придумать как получше сделать
            DataGridColumn newColumn;
            DataTemplate headerTemplate = null;

            if (info.HeaderTemplateName != null)
                headerTemplate = (DataTemplate)FindResource(info.HeaderTemplateName);
            if (info.TemplateName == "bool")
                newColumn = new DataGridCheckBoxColumn
                                {
                                    Binding = bindingBase,
                                    HeaderTemplate = headerTemplate
                                };
            else if (ColumnInfoTemplates.IsStandart(info.TemplateName))
                newColumn = new ExtendedTextColumn
                                {
                                    HorizontalAlignment = ColumnAlignment(info),
                                    VerticalAlignment = VerticalAlignment.Center,
                                    Binding = bindingBase,
                                    TextWrap = info.TemplateName == ColumnInfoTemplates.StringWrap,
                                    HeaderTemplate =  headerTemplate
                                };
            else
                newColumn = new BindingTemplatedColumn { Binding = new Binding(info.PropertyPath),
                    CellTemplate = (DataTemplate)FindResource(info.TemplateName), HeaderTemplate =  headerTemplate};
            return newColumn;
        }

        private static HorizontalAlignment ColumnAlignment(IColumnInfo info)
        {
            var tp = info.ExcelDataType();
            if (tp == "int" || tp == "number") return HorizontalAlignment.Right;
            return HorizontalAlignment.Left;
        }

        protected override void OnExecutedCopy(ExecutedRoutedEventArgs args)
        {
            Clipboard.Clear();
            Clipboard.SetText(CurentCellValue);
        }
    }

    public class ExtendedTextColumn : DataGridTextColumn
    {
        public HorizontalAlignment HorizontalAlignment { get; set; }
        public VerticalAlignment VerticalAlignment { get; set; }
        public bool TextWrap { get; set; }

        protected override FrameworkElement
            GenerateElement(DataGridCell cell, object dataItem)
        {
            var element = (TextBlock)base.GenerateElement(cell, dataItem);

            element.HorizontalAlignment = HorizontalAlignment;
            element.VerticalAlignment = VerticalAlignment;
            element.TextWrapping = TextWrap ? TextWrapping.Wrap : TextWrapping.NoWrap;

            return element;
        }

        protected override FrameworkElement
            GenerateEditingElement(DataGridCell cell, object dataItem)
        {
            var textBox = (TextBox)base.GenerateEditingElement(cell, dataItem);

            textBox.TextAlignment = GetTextAlignment();
            textBox.VerticalContentAlignment = VerticalAlignment;
            textBox.TextWrapping = TextWrap ? TextWrapping.Wrap : TextWrapping.NoWrap;

            return textBox;
        }

        private TextAlignment GetTextAlignment()
        {
            if (HorizontalAlignment == HorizontalAlignment.Left) return TextAlignment.Left;
            if (HorizontalAlignment == HorizontalAlignment.Right) return TextAlignment.Right;
            if (HorizontalAlignment == HorizontalAlignment.Center) return TextAlignment.Center;
            return TextAlignment.Justify;
        }
    }

    public class BindingTemplatedColumn: DataGridTemplateColumn
    {
        public BindingBase Binding { get; set; }
        
        protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
        {
            var element = base.GenerateElement(cell, dataItem);
            BindingOperations.SetBinding(element, CellDataAttachedProperty.CellDataProperty, Binding);
            return element;
        }
    }
}